package de.ff7rl.ui;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.font.FontRenderContext;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;

import javax.swing.JComponent;

public class TerminalComponent extends JComponent implements Terminal {

  private final int maxCols;
  private final int maxLines;
  private final int charWidth;
  private final int charHeight;
  private TerminalData terminalData;

  public TerminalComponent(final int theFontSize, final int theMaxLines, final int theMaxCols,
      final Color theForegroundColor, final Color theBackgroundColor) {
    maxLines = theMaxLines;
    maxCols = theMaxCols;
    final Font font = new Font(Font.MONOSPACED, Font.BOLD, theFontSize);
    setFont(font);
    final AffineTransform atrans = font.getTransform();
    final FontRenderContext frContext = new FontRenderContext(atrans, true, true);
    final Rectangle2D stringBounds = font.getStringBounds(Character.toString('M'), frContext);
    final double width = stringBounds.getWidth();
    final double height = stringBounds.getHeight();
    charWidth = (int) Math.round(width) + 2;
    charHeight = (int) height;
    terminalData = new TerminalData(new char[theMaxLines][theMaxCols], theForegroundColor, theBackgroundColor,
        new Color[theMaxLines][theMaxCols], new Color[theMaxLines][theMaxCols]);
    final Dimension d = new Dimension(charWidth * theMaxCols, charHeight * theMaxLines);
    setPreferredSize(d);
    setForeground(theForegroundColor);
    setBackground(theBackgroundColor);
    setEnabled(true);
    clear();
  }

  @Override
  public void clear() {
    for (int i = 0; i < maxLines; i++) {
      for (int j = 0; j < maxCols; j++) {
        terminalData.getCharacters()[i][j] = ' ';
        terminalData.getBackgroundColors()[i][j] = terminalData.getBackgroundColor();
        terminalData.getForegroundColors()[i][j] = terminalData.getForegroundColor();
      }
    }
    repaint();
  }

  @Override
  public Point getTerminalSize() {
    return new Point(maxCols, maxLines);
  }

  @Override
  public void paint(final Graphics g) {
    final Graphics2D g2 = (Graphics2D) g;
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    for (int i = 0; i < maxLines; i++) {
      for (int j = 0; j < maxCols; j++) {
        final char currentChar = terminalData.getCharacters()[i][j];
        final Color foreground = terminalData.getForegroundColors()[i][j];
        final Color background = terminalData.getBackgroundColors()[i][j];
        printChar(g, i, j, currentChar, foreground, background);
      }
    }
  }

  @Override
  public void restore(final TerminalData theTerminalData) {
    if (theTerminalData == null) {
      throw new IllegalStateException("No terminal data given to restore.", new NullPointerException());
    }
    terminalData = new TerminalData(theTerminalData);
    repaint();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public TerminalData store() {
    return new TerminalData(terminalData);
  }

  @Override
  public void write(final int theX, final int theY, final String theText) {
    write(theX, theY, theText, terminalData.getForegroundColor());
  }

  @Override
  public void write(final int theX, final int theY, final String theText, final Color theForegroundColor) {
    write(theX, theY, theText, theForegroundColor, terminalData.getBackgroundColor());
  }

  @Override
  public void write(final int theX, final int theY, final String theText, final Color theForegroundColor,
      final Color theBackgroundColor) {
    for (int i = 0; i < theText.length(); i++) {
      terminalData.getCharacters()[theY][theX + i] = theText.charAt(i);
      terminalData.getForegroundColors()[theY][theX + i] = theForegroundColor;
      terminalData.getBackgroundColors()[theY][theX + i] = theBackgroundColor;
    }
    repaint();
  }

  private int lineToPixels(final int line) {
    return charHeight * line;
  }

  private int posToPixels(final int pos) {
    return charWidth * pos;
  }

  private void printChar(final Graphics g, final int line, final int pos, final char chr, final Color foreground,
      final Color background) {
    final int startX = posToPixels(pos);
    final int startY = lineToPixels(line);

    g.setColor(background);
    g.fillRect(startX, startY, charWidth, charHeight);

    g.setColor(foreground);
    g.drawChars(new char[] { chr }, 0, 1, startX + 1, startY + charHeight * 2 / 3);
  }

}
